# 44. 异常处理

# 异常处理

异常:一旦程序中发生了异常,那么程序就立刻停止,不继续执行下去

异常分为:异常跟错误

  1. 异常:Iteration 异常 在执行代码的过程中引发的异常
  2. 错误:Error 语法错误 比较明显的错误 在编译代码阶段就能检测出来

# 异常信息

常见的异常信息

  1. AttributeError:试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x
  2. IOError: 输入/输出异常;基本上是无法打开文件
  3. ImportError: 无法引入模块或包;基本上是路径问题或名称错误
  4. IndentationError: 语法错误(的子类) ;代码没有正确对齐
  5. IndexError :下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
  6. KeyError: 试图访问字典里不存在的键
  7. KeyboardInterrupt: Ctrl+C被按下
  8. NameError: 使用一个还未被赋予对象的变量
  9. SyntaxError: Python代码非法,代码不能编译(个人认为这是语法错误,写错了)
  10. TypeError: 传入对象类型与要求的不符合
  11. UnboundLocalError: 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量, 导致你以为正在访问它
  12. ValueError: 传入一个调用者不期望的值,即使值的类型是正确的

异常信息

这里就只说异常的名字,如果想了解,自行官网文档

异常名 异常名 异常名
ArithmeticError IndentationError StandardError
AssertionError IndexError StopIteration
AttributeError IOError SyntaxError
BaseException KeyboardInterrupt SyntaxWarning
BufferError KeyError SystemError
BytesWarning LookupError SystemExit
DeprecationWarning MemoryError TabError
EnvironmentError NameError TypeError
EOFError NotImplementedError UnboundLocalError
Exception OSError UnicodeDecodeError
FloatingPointError OverflowError UnicodeEncodeError
FutureWarning PendingDeprecationWarning UnicodeError
GeneratorExit ReferenceError UnicodeTranslateError
ImportError RuntimeError UnicodeWarning
ImportWarning RuntimeWarning UserWarning
Warning ValueError ZeroDivisionError

# 简单的异常处理

so = ["查看","删除","增加","修改"]
for i in enumerate(so,1):
    print(i[0],i[1])
try:
    user = int(input("》》》:"))
    print(so[user-1])
except ValueError:
    print("请输入一个数字")
    
执行结果:
1 查看
2 删除
3 增加
4 修改
》》》:a
请输入一个数字

## 这种异常处理:能够处理用户输入时,非数字的情况下,如果在外面在加个循环,报错后,在让用户输入,这样子,用户体验会相对更好

# 多分支异常处理

so = ["查看","删除","增加","修改"]
for i in enumerate(so,1):
    print(i[0],i[1])
try:
    user = int(input("》》》:"))
    print(so[user-1])
except ValueError:
    print("请输入一个数字")
except IndexError:
    print('您输入的数字无效')
    
执行结果:
1 查看
2 删除
3 增加
4 修改
》》》:6
您输入的数字无效
》》》:a
请输入一个数字

## 这样子,如果输入超范围的数字的报错,也截下来,转成自己输出的话,提高用户体验

# 万能异常

so = ["查看","删除","增加","修改"]
for i in enumerate(so,1):
    print(i[0],i[1])
try:
    user = int(input("》》》:"))
    print(so[user-1])
except Exception:
    print("您输入有误。。。。。")
    
执行结果:
1 查看
2 删除
3 增加
4 修改
》》》:6
您输入有误。。。。。

## 万能异常:Exception,Exception是所有异常的父类,可以说每个异常都是一个类,但异常的类都继承Exception,所以Exception能处理几乎所有的异常

# 万能异常和其他分支合作

注意:万能异常和其他分支合作,万能异常永远要放在所有except 异常的最后

so = ["查看","删除","增加","修改"]
for i in enumerate(so,1):
    print(i[0],i[1])
while 1:
    try:
        user = int(input("》》》:"))
        print(so[user-1])
    except ValueError:
        print("请输入一个数字")
    except Exception:
        print("您输入有误。。。。。")
        
执行结果:
1 查看
2 删除
3 增加
4 修改
》》》:a
请输入一个数字
》》》:6
您输入有误。。。。。

为什么万能异常在放在其他异常的后面,这就要说到异常处理顺序

异常处理顺序:能 if判断 一样,一旦执行匹配到,就会立刻跳到匹配到的,不会在匹配接下来的

如果万能异常放在最上面,那下面还写不写异常有什么区别吗

# 异常处理的其他机制

# else

try中的代码正常执行 没有异常的时候会执行else中的代码

so = ["查看","删除","增加","修改"]
for i in enumerate(so,1):
    print(i[0],i[1])
while 1:
    try:
        user = int(input("》》》:"))
        print(so[user-1])
    except ValueError:
        print("请输入一个数字")
    except Exception:
        print("您输入有误。。。。。")
    else:
        print("执行了%s功能"%so[user-1])
        
执行结果:
1 查看
2 删除
3 增加
4 修改
》》》:1
查看
执行了查看功能

# finally

无论try如何都会执行,无论在try中报的错,没有截到,程序要自动停止,finally还是会执行的

一般用处:操作系统资源归还的工作方面

so = ["查看","删除","增加","修改"]
for i in enumerate(so,1):
    print(i[0],i[1])
while 1:
    try:
        user = int(input("》》》:"))
        print(so[user-1])
    except ValueError:
        print("请输入一个数字")
    else:
        print("执行了%s功能"%so[user-1])
    finally:
        print("无论try如何都会执行")
        
执行结果:
1 查看
2 删除
3 增加
4 修改
》》》:6
无论try如何都会执行
IndexError: list index out of range
    
## 就算是报错了,finally还是会执行

# 异常处理的执行顺序

try:
except:

try:
except:
else:
    
try:
except:
else:
finally:

try:
except:
finally:

try:
finally:

# 主动抛异常

raise 如果不指定抛出的错误,默认抛出截到的错误

注意:raise 指定抛出的错误只能是定义好的错误

## raise 默认抛出截到的错误
so = ["查看","删除","增加","修改"]
for i in enumerate(so,1):
    print(i[0],i[1])
while 1:
    try:
        user = int(input("》》》:"))
        print(so[user-1])
    except ValueError:
        print("请输入一个数字")
        raise
    else:
        print("执行了%s功能"%so[user-1])
        
执行结果:
1 查看
2 删除
3 增加
4 修改
》》》:a
请输入一个数字
ValueError: invalid literal for int() with base 10: 'a'
        
        
## raise 指定抛出错误
so = ["查看","删除","增加","修改"]
for i in enumerate(so,1):
    print(i[0],i[1])
while 1:
    try:
        user = int(input("》》》:"))
        print(so[user-1])
    except ValueError:
        print("请输入一个数字")
        raise IndentationError
    else:
        print("执行了%s功能"%so[user-1])
        
执行结果:
1 查看
2 删除
3 增加
4 修改
》》》:a
请输入一个数字
IndentationError: None
  

## 直接抛出
raise IndentationError

执行结果:
IndentationError: None

# 自定义异常

格式:class 名称(Exception):Exception是所有异常错误类的父类

## 简单使用
class Customerror(Exception):
    pass
raise Customerror

执行结果:
    raise Customerror
__main__.Customerror

## 指定报错信息
class Customerror(Exception):
    def __init__(self,msg):
        self.msg = msg

raise Customerror("这是我自定义的异常错误")

执行结果:
raise Customerror("这是我自定义的异常错误")
__main__.Customerror: 这是我自定义的异常错误
## 想要自定义错误信息,就必须要有__init__这个方法跟msg这个参数的存在,参数名可自定义

# 断言

断言很简单:

断言是以布尔值为准,简单说,断言为True,接着执行下面的代码,断言为False,报错停止,不执行下面的代码

## 断言为True
print(11)
assert True
print(12)

执行结果:
11
12

## 断言为False
print(11)
assert False
print(12)

执行结果:
11
assert False
AssertionError

# 异常处理总结

  1. 尽量少用异常处理
  2. 能通过逻辑规避的应该代码逻辑规避掉
  3. 应该对某一句/几句话来进行处理